
#include <avr/io.h>
#include "common.h"
#include "vm.h"
#include "delay.h"
#include "prand.h"
#include "sleep.h"

// status flags meant to losely resemble those on a 6502
#define ST_C 0
#define ST_E 1
#define ST_M 2
#define ST_P 3
#define ST_V 4

// internal status flag
#define IST_EXIT 0
#define IST_DEEPSLEEP 1
#define IST_ERROR 2

uint8_t pc, a, st, ist;		// program count, accumulator value, and status register
uint8_t program[40];		// program space, uses char so we can just treat programs as strings
uint8_t reg[16];

void wipe_program() {
	pc=0;
	st=0;
	ist=0;
	a=0;
	for(int i=0; i<sizeof(program); i++) {
		program[i]=0;
	}
	for(int i=0; i<sizeof(reg); i++) {
		reg[i]=0;
	}
}

// XXX this is dumb as poop run loop for now
void run_program() {
	while(1) {
		step_program();
	}
}

// run exactly one instruction of a loaded program
void step_program() {
	uint8_t op, compop, nextpe;	// we need to both preserve the op, and normalize the compressed ones down
					// pe is an optimization trick
	op = program[pc];		// get the op
	compop = op;			// copy it for mutation
	// normalize the ops...
	if(compop >= 0xc0) {			// compressed JMPs
		compop = 0xc0;
	} else if(compop >= 0xb0) {		// compressed SR
		compop = 0xb0;
	} else if(compop >= 0xa0) {		// compressed SRA
		compop = 0xa0;
	} else if(compop >= 0x90) {		// compressed SAR
		compop = 0x90;
//	} else if(compop >= 0x80) {		// compressed ??? not yet defined
//		compop = 0x80;
	}
	// bump the pc a step, the routines for the ops are responsible for pushing past their params..
	pc++;
	nextpe = program[pc];		// get the first potential param so others dont need to...
	// compop is normal now, do the big-switch
	switch(compop) {
		case 0xC0: 				// JMP
			pc = op & 0x3f;			// strip 2 high bits off 
			break;
		case 0xB0: 				// SR
			reg[op & 0x0f] = nextpe;	// strip 4 high bits off, index, and set 
			pc++;
			break;
		case 0xA0: 				// SRA
			reg[op & 0x0f] = a;		// strip 4 high bits off, index, and set 
			break;
		case 0x90: 				// SAR
			a = reg[op & 0x0f];		// strip 4 high bits off, index, and set 
			break;
		case 0x7F: 				// IDLY
			delay( ((uint32_t )reg[(nextpe & 0x0f)]) << ((nextpe & 0xf0) >> 4)   );
			pc++;
			break;
		case 0x7E: 				// DLY16
			delay(((uint32_t)nextpe) << 16);
			pc++;
			break;
		case 0x7D: 				// DLY
			delay(nextpe);
			pc++;
			break;
		case 0x7C: 				// DLY8
			delay(((uint32_t)nextpe) << 8);
			pc++;
			break;
		case 0x7B: 				// RED
			VALR = nextpe;
			pc++;
			break;
		case 0x7A: 				// GREEN
			VALG = nextpe;
			pc++;
			break;
		case 0x79: 				// BLUE
			VALB = nextpe;
			pc++;
			break;
		case 0x78: 				// BRIGHT
			VALI = nextpe;
			pc++;
			break;
		// special case, RGBI takes the intensity value first, and fall into RGB code
		case 0x77: 				// RGBI
			VALI = nextpe;
			pc++;
		case 0x76: 				// RGB
			VALR = program[pc];		// can't use nextpe here as RGBI may have yoinked it
			pc++;
			VALG = program[pc];
			pc++;
			VALB = program[pc];
			pc++;
			break;
		case 0x75: 				// IRED
			VALR = reg[nextpe&0x0f];
			pc++;
			break;
		case 0x74: 				// IGREEN
			VALG = reg[nextpe&0x0f];
			pc++;
			break;
		case 0x73: 				// IBLUE
			VALB = reg[nextpe&0x0f];
			pc++;
			break;
		case 0x72: 				// IBRIGHT
			VALI = reg[nextpe&0x0f];
			pc++;
			break;
		case 0x71: 				// CRGB
			VALR = (nextpe & 0xf0);
			VALG = (nextpe & 0x0c) << 4;
			VALB = (nextpe & 0x03) << 6;
			pc++;
			break;
		case 0x70: 				// CRGBI
			VALR = (nextpe & 0xc0);
			VALG = (nextpe & 0x30) << 2;
			VALB = (nextpe & 0x0c) << 4;
			VALI = (nextpe & 0x03) << 6;
			pc++;
			break;

		case 0x65: 				// BEQ
			if(bitRead(st, ST_E)) {
				pc = nextpe;
			} else {
				pc++;
			}
			break;
		case 0x63: 				// BNE
			if(! bitRead(st, ST_E)) {
				pc = nextpe;
			} else {
				pc++;
			}
			break;
		case 0x5F: 				// RNDA
			a = prand();
			break;
		// special case, IRGBI falls through to RGBI, looking a bit ahead for it's part
		case 0x57: 				// IRGBI
			VALI = reg[ program[pc+1] & 0x0f ];
		case 0x56: 				// IRGB
			VALR = reg[(nextpe & 0xf0)>>4];
			VALG = reg[nextpe & 0x0f];
			pc++;
			VALB = reg[(program[pc]&0xf0)>>4];
			pc++;
			break;
		case 0x55: 				// STEPTO
		{					// scoping!
			uint8_t v,t;
			v = (nextpe & 0xf0)>>4;	// value register on the high nibble
			t = nextpe & 0x0f;
			pc++;
			if(reg[v] > reg[t]) {		// over target
				reg[v]--;
				st |= (1<<ST_P);		// set flags as like CMP would
				st &=~ (1<<ST_M | 1<<ST_E);
			} else if(reg[v] < reg[t]) {    // under target
				reg[v]++;
				st |= (1<<ST_M);
				st &=~ (1<<ST_P | 1<<ST_E);
			} else {			// we need to handle equal case
				st |= (1<<ST_E);
				st &=~ (1<<ST_P | 1<<ST_M);
			}
		}
			break;
		case 0x02: 				// DEEPSLEEP
			// do nothing.. putting the NO in NOP
			//ist |= IST_DEEPSLEEP;
			deepsleep();
			break;
		case 0x01: 				// EOP
			// do nothing.. putting the NO in NOP
			break;
		case 0x00: 				// EOP
			pc = 0x00;			// loops program to beginning
			break;
		default:
			break;
	}
	// any post-instruction operations...
}

